package base.thread_example.example02;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 *	jdk1.5后，一个更高级的同步锁。
 *	Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。
 *
 * 虽然我们可以理解同步代码块和同步方法的锁对象问题，但是我们并没有直接看到在哪里加上了锁，在哪里释放了锁，
 * 为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock。
 *
 * Lock：
 * 		void lock()： 获取锁。
 * 		void unlock():释放锁.
 * ReentrantLock是Lock的实现类.
 *
 */
public class TicketSell {

    public static void main(String[] args) {
        TicketSell ts = new TicketSell();
        // 创建资源对象
        TicketSell.Ticket ticket = ts.new Ticket(1000);
        // 创建任务协议对象
        TicketSell.TicketRunnable runnable = ts.new TicketRunnable(ticket);

        // 创建4个卖票窗口
        new Thread( runnable,"窗口1").start();
        new Thread( runnable,"窗口2").start();
        new Thread( runnable,"窗口3").start();
        new Thread( runnable,"窗口4").start();
    }



    class TicketRunnable implements Runnable{
        private final Logger log = LoggerFactory.getLogger(TicketRunnable.class);
        private Ticket ticket;
        private Lock lock = new ReentrantLock();
        public TicketRunnable(Ticket ticket){
            this.ticket = ticket;
        }

        @Override
        public void run() {
            while (true){
                try {
                    lock.lock();

                    if(ticket.getNum()>0)
                        ticket.sell();
                    else
                        break;
                }finally {
                    // 释放锁
                    lock.unlock();
                }
            }
        }
    }

    class Ticket {
        private final Logger log = LoggerFactory.getLogger(Ticket.class);
        private int num;// 票数
        public Ticket(int num){
            this.num = num;
        }

        public void sell(){
            if(this.num <= 0){
                return;
            }
            log.info("{} 正在出售第 {} 张票", Thread.currentThread().getName(),this.num);
            num--;
        }

        public int getNum() {
            return num;
        }
        public void setNum(int num) {
            this.num = num;
        }
    }
}
